
#include <cstdio>
#include <BOpera>
#include <BTextEdit>

#include "TextFormat.h"

Align StringToAlign(const BString& text)
{
	Align align = Align_None;
	if (text == "none" || text == "empty")
		align = Align_None;
	else if (text == "top")
		align = Align_Top;
	else if (text == "left")
		align = Align_Left;
	else if (text == "right")
		align = Align_Right;
	else if (text == "bottom")
		align = Align_Bottom;
	else if (text == "center")
		align = Align_Center;
	else if (text == "left-center")
		align = Align_LeftCenter;
	else if (text == "right-center")
		align = Align_RightCenter;
	else if (text == "top-center")
		align = Align_TopCenter;
	else if (text == "bottom-center")
		align = Align_BottomCenter;
	else if (text.contain(','))
	{
		BString word;
		int pos = 0;
		while (pos < text.size())
		{
			pos = text.word(word, pos);
			if (word == ',')
				continue;
			else if (word == "left")
				((int&)align) |= Align_Left;
			else if (word == "right")
				((int&)align) |= Align_Right;
			else if (word == "top")
				((int&)align) |= Align_Top;
			else if (word == "bottom")
				((int&)align) |= Align_Bottom;
			else if (word == "hcenter")
				((int&)align) |= Align_HCenter;
			else if (word == "vcenter")
				((int&)align) |= Align_VCenter;
		}
	}
	return Align_None;
}
BString StringFromAlign(Align align)
{
	switch (align)
	{
	case Align_None:			return "none";			
	case Align_Left:			return "left";			
	case Align_Right:			return "right";		
	case Align_HCenter:			return "hcenter";		
	case Align_Top:				return "top";			
	case Align_Bottom:			return "bottom";		
	case Align_VCenter:			return "vcenter";		
	case Align_LeftTop:			return "left-top";		
	case Align_LeftCenter:		return "left-center";	
	case Align_LeftBottom:		return "left-bottom";	
	case Align_RightTop:		return "right-top";	
	case Align_RightCenter:		return "right-center";	
	case Align_RightBottom:		return "right-bottom";	
	case Align_TopCenter:		return "top-center";	
	case Align_BottomCenter:	return "bottom-center";
	case Align_Center:			return "center";		
	default:					return 0;		
	}
}

BString StringFromColor(const BColor& color)
{
	BString text = '#';
	char cstr[4];
	bByteToHex(cstr, color.r()); text << cstr;
	bByteToHex(cstr, color.g()); text << cstr;
	bByteToHex(cstr, color.b()); text << cstr;
	return text;
}
BColor StringToColor(const BString& text)
{
	BColor color;
	BString temp = text;
	if (temp.beginWith('#'))
	{
		temp.remove('#');
		if (temp.size() == 6)
		{
			color.r() = bHexToByte(temp.cstr());
			color.g() = bHexToByte(temp.cstr() + 2);
			color.b() = bHexToByte(temp.cstr() + 4);
			color.a() = 255;
		}
		if (temp.size() == 8)
		{
			color.r() = bHexToByte(temp.cstr());
			color.g() = bHexToByte(temp.cstr() + 2);
			color.b() = bHexToByte(temp.cstr() + 4);
			color.a() = bHexToByte(temp.cstr() + 6);
		}
	}
	return color;
}

BString StringFromFont(const BFont* font)
{
	BString text;
	text << font->family() << ',' << font->size();
	return text;
}
BFont* StringToFont(const BString& text)
{
	BFont* font = new BFont();
	BStringArray words;
	text.split(words, ',');
	for (int i = 0; i < words.size(); i++)
	{
		const BString& word = words[i];
		if (word.isNumber())
		{
			int size = word.toInt();
			font->setSize(size);
			continue;
		}
		font->setFamily(word);
	}
	return font;
}

TextFormat::TextFormat() : BTextFormat()
{

}
TextFormat::~TextFormat()
{

}

void TextFormat::styled(const BStyle* style)
{

}
void TextFormat::changed(const BString& text)
{

}

const BFont* TextFormat::font(int pos) const
{
	auto it = _codeItemMap <= pos;
	if (it.valid())
	{
		if (pos < it.key() + it->count)
			return it->font;
	}
	return 0;
}
const BColor* TextFormat::color(int pos) const
{
	auto it = _codeItemMap <= pos;
	if (it.valid())
	{
		if (pos < it.key() + it->count)
			return &it->color;
	}
	return 0;
}
const BColor* TextFormat::ground(int pos) const
{
	auto it = _codeItemMap <= pos;
	if (it.valid())
	{
		if (pos < it.key() + it->count)
			return &it->ground;
	}
	return 0;
}

Align TextFormat::lineAlign(int line) const
{
	auto it = _lineItemMap == line;
	return it->align;
}
int TextFormat::lineSpacing(int line) const
{
	auto it = _lineItemMap == line;
	return it->spacing;
}
int TextFormat::lineHeight(int line) const
{
	auto it = _lineItemMap == line;
	return it->height;
}

void TextFormat::setLineAlign(int line, Align align)
{
	LineItem& item = _lineItemMap[line];
	if (item.align != align)
	{
		item.align = align;
		this->emit(Signal_Changed);
	}
}
void TextFormat::setLineSpacing(int line, int spacing)
{
	LineItem& item = _lineItemMap[line];
	if (item.spacing != spacing)
	{
		item.spacing = spacing;
		this->emit(Signal_Changed);
	}
}
void TextFormat::setLineHeight(int line, int height)
{
	LineItem& item = _lineItemMap[line];
	if (item.height != height)
	{
		item.height = height;
		this->emit(Signal_Changed);
	}
}

void TextFormat::setFont(int pos, int count, BFont* font)
{
	CodeItem& item = _codeItemMap[pos];
	item.count = count;
	if (item.font != font)
	{
		item.font = font;
		this->emit(Signal_Changed);
	}
}
void TextFormat::setColor(int pos, int count, const BColor& color)
{
	CodeItem& item = _codeItemMap[pos];
	item.count = count;
	if (item.color != color)
	{
		item.color = color;
		this->emit(Signal_Changed);
	}
}
void TextFormat::setGround(int pos, int count, const BColor& ground)
{
	CodeItem& item = _codeItemMap[pos];
	item.count = count;
	if (item.ground != ground)
	{
		item.ground = ground;
		this->emit(Signal_Changed);
	}
}

const BMap<int, CodeItem>& TextFormat::codeItemMap() const
{
	return _codeItemMap;
}
const BMap<int, LineItem>& TextFormat::lineItemMap() const
{
	return _lineItemMap;
}

void TextFormat::slotTextEditOpera(BWidget* widget, const BValue& value)
{
	BOpera* opera = value;
	if (opera->mode == BOpera::Mode_Input)
	{
		auto it = _codeItemMap >= opera->index;
		while (it.valid())
		{
			it.key()++;
			++it;
		}
		this->emit(Signal_Changed);
	}
	if (opera->mode == BOpera::Mode_Insert)
	{
		int count = opera->text.count();
		auto it = _codeItemMap >= opera->index;
		while (it.valid())
		{
			it.key() += count;
			++it;
		}
		this->emit(Signal_Changed);
	}
	if (opera->mode == BOpera::Mode_Remove)
	{
		int count = opera->text.count();
		auto it = _codeItemMap >= opera->index;
		while (it.valid())
		{
			it.key() -= count;
			++it;
		}
		this->emit(Signal_Changed);
	}
	if (opera->mode == BOpera::Mode_Delete)
	{
		int count = opera->text.count();
		auto it = _codeItemMap >= opera->index;
		while (it.valid())
		{
			it.key() -= count;
			++it;
		}
		this->emit(Signal_Changed);
	}
}
void TextFormat::slotTextSelectionChanged(BWidget* widget, const BValue& value)
{
	BTextEdit* textEdit = (BTextEdit*)this->host();
	BRange range = value;

	const CodeItem* codeItem = 0;
	auto cit = this->codeItemMap() <= range.min();
	if (cit.valid())
		codeItem = &(*cit);
	this->emit("code-item-changed", codeItem);

	const LineItem* lineItem = 0;
	auto lit = this->lineItemMap() <= range.min();
	if (lit.valid())
		lineItem = &(*lit);
	this->emit("line-item-changed", lineItem);

}

void TextFormat::clear()
{
	_codeItemMap.clear();
	_lineItemMap.clear();
	this->emit(Signal_Clear);
	this->emit(Signal_Changed);
}

void TextFormat::attached(BWidget* host)
{
	connect(host, Signal_Opera, &TextFormat::slotTextEditOpera);
	connect(host, Signal_SelectionChanged, &TextFormat::slotTextSelectionChanged);
}
void TextFormat::detached(BWidget* host)
{
	disconnect(host);
}
